using Abp.Authorization;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.UI;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Application.Dto;
using Mt.Site.Application.FileUpload.Dto;
using Mt.Site.Core.Base.BlobStoring;
using Mt.Site.Core.Base.Settings;
using Mt.Site.Core.Base.Utils.FileIO.Upload;
using Mt.Site.Core.CustomConfigurations;
using Mt.Site.Core.Storage;
using Mt.Site.Core.Storage.File_;
using Mt.Site.Core.Synchronization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Mt.Site.Core.Base.Utils.FileIO.Upload.UpfileTemplate;

namespace Mt.Site.Application.FileUpload.Impl
{
    /// <summary>
    /// 1. 在新增内容时，先随机一个Guid, 作为附件上传控件要使用得内容id, 在内容信息添加完成后，再将这个随机的id替换为真实的id, 同时可以做数据同步
    /// 2. 在内容信息已存在时，如何同步新增的附件：文件上传控件接受一个外层Scope的函数，当完成上传操作时，主动调用这个函数，触发同步等操作？
    /// </summary>

    [AbpAuthorize]
    public class FileUploadAppService : MtSiteAppServiceBase, IFileUploadAppService
    {
        private readonly IFileLibraryManager _fileLibraryManager;
        private readonly IFileObjectManager _fileObjectManager;

        //private readonly IImageHelper _imageHelper;

        private readonly IFileUploadHelper _fileUploadHelper;

        private readonly IBlobContainer _container;

        public FileUploadAppService(
            IFileLibraryManager fileLibraryManager,
            IFileObjectManager fileObjectManager,
            IFileUploadHelper fileUploadHelper,
            IBlobContainer container)
        {
            _fileLibraryManager = fileLibraryManager;
            _fileObjectManager = fileObjectManager;

            _fileUploadHelper = fileUploadHelper;
            _container = container;
        }

        #region 查找

        /// <summary>
        /// 获取文件信息列表
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<FileInfoDto>> GetFileInfos(GetFileInfoInput input)
        {
            var query = GetFileObjectQuery(input);
            return await query.ToListAsync();
        }
        /// <summary>
        /// FirstOrDefault
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<FileInfoDto> GetFileInfo(GetFileInfoInput input)
        {
            var query = GetFileObjectQuery(input);
            return await query.FirstOrDefaultAsync();
        }

        public async Task<FileInfoDetailDto> GetFileInfoById(Guid id)
        {
            var fileObject = await _fileObjectManager.GetOrNullAsync(id);
            if(fileObject != null)
            {
                return ObjectMapper.Map<FileInfoDetailDto>(fileObject);
            }
            return null;
        }

        #endregion


        #region 文件库选择文件

        /// <summary>
        /// 上传文件（批量）
        /// </summary>
        /// <returns></returns>
        public async Task<FileInfoDto> UploadFile2(SelectFromLibraryInput input)
        {
            await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            if (input.FileUrl.IsNullOrWhiteSpace())
            {
                throw new UserFriendlyException("文件不能为空");
            }

            var fileName = Path.GetFileName(input.FileUrl);
            var exName = Path.GetExtension(fileName).RemovePreFix(".");

            var fileInfoDto = await _fileUploadHelper.CreateFileInfo2(_fileObjectManager, input, fileName, exName);

            await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

            return fileInfoDto;
        }

        /// <summary>
        /// 单个文件上传
        /// </summary>
        /// <returns></returns>
        public async Task<FileInfoDto> SingleUpload2(SelectFromLibraryInput input)
        {
            await _fileUploadHelper.GetUpFileConfig(input.CustomKey, null);

            if (input.FileUrl.IsNullOrWhiteSpace())
            {
                throw new UserFriendlyException("文件不能为空");
            }

            var fileName = Path.GetFileName(input.FileUrl);
            var exName = Path.GetExtension(fileName).RemovePreFix(".");

            var fileInfoDto = await _fileUploadHelper.CreateFileInfo2(_fileObjectManager, input, fileName, exName);

            await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(input.ContentId, fileInfoDto.Id, OptionType.Create, AbpSession);

            return fileInfoDto;
        }

        #endregion

        #region 编辑，删除

        public async Task UpdateFileInfo(UpFileInfoInput input)
        {
            var entity = await _fileObjectManager.GetAsync(input.Id);
            entity.Title = input.Title;
            entity.Description = input.Description;
            entity.LinkUrl = input.LinkUrl;
            entity.Order = input.Order;
            entity.IsActive = input.IsActive;
            entity.AutoSynchronous = input.AutoSynchronous;
            entity.Alt = input.Alt;
            entity.Scoure = input.Source;

            string oldFileUrl = null;

            if (!input.FileUrl.IsNullOrWhiteSpace() && input.FileUrl != entity.FileUrl)
            {
                var para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

                var fileInfo = await _fileLibraryManager.RemoveTemporaryDataAndGetInfoAsync(input.FileUrl);

                oldFileUrl = entity.FileUrl;

                entity.FileUrl = input.FileUrl;
                if(fileInfo != null)
                {
                    entity.Size = fileInfo.Dimensions;
                    entity.FileLength = fileInfo.FileSize;
                    entity.exName = fileInfo.ExName;
                    entity.FileName = fileInfo.FileName;
                    entity.Sha1 = fileInfo.Sha1;
                    entity.IsFromLibrary = fileInfo.IsFromLibrary;
                }

                await _fileUploadHelper.OnEdited(entity.FileUrl, entity.Id, false);
            }

            if (!input.PreViewImgUrl.IsNullOrWhiteSpace())
            {
                // 原文件在文件库中，不需要删
                if (input.PreViewImgUrl != entity.PreViewImage)
                {
                    var para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

                    await _fileLibraryManager.RemoveTemporaryDataAsync(input.PreViewImgUrl);

                    entity.PreViewImage = input.PreViewImgUrl;
                    await _fileUploadHelper.OnEdited(entity.PreViewImage, entity.Id, true);
                }
            }
            else
            {
                entity.PreViewImage = null;
            }

            await _fileObjectManager.RemoveTemporaryFileAsync();
            await _fileObjectManager.TryDeleteFile(oldFileUrl, entity.Id, _fileLibraryManager);

            await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(entity.ContentId, entity.Id, OptionType.Update, AbpSession);
        }

        /// <summary>
        /// 修改文件
        /// 文件拷贝原则：先拷贝一份到临时文件夹进行重命名，然后将新文件拷贝回原目录（防止无限文件夹创建）
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<FileRenameOut> UpdateFileName(updateFileNameInput input)
        {
            var entity = await _fileObjectManager.GetOrNullAsync(input.Id);

            var fileRenameDto = new FileRenameOut();
            if (entity != null)
            {
                input.NewFileName = input.NewFileName.RemoveBadCharOfFileName();
                var originUrl = entity.FileUrl;
                var strArr = originUrl.Split('/').ToList();
                var originFileName = strArr.Last();
                var newFileName = input.NewFileName + "." + originFileName.Split(".").Last();
                strArr.RemoveAt(strArr.Count - 1);
                var newFilePath = strArr.JoinAsString("/") + "/" + newFileName;

                var isExist = await _container.ExistsAsync(newFilePath);
                if (isExist)
                {
                    throw new UserFriendlyException(L("FileNameIsExist"));
                }

                await _container.CopyAsync(originUrl, newFilePath, false);
               
                await _fileObjectManager.TryDeleteFile(originUrl, entity.Id, _fileLibraryManager);

                entity.FileUrl = newFilePath;
                entity.FileName = newFileName;
                await _fileObjectManager.UpdateAsync(entity);

                fileRenameDto.TargetUrl = newFilePath;

                await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(entity.ContentId, entity.Id, OptionType.Update, AbpSession);
            }

            await _fileObjectManager.RemoveTemporaryFileAsync();

            return fileRenameDto;
        }

        /// <summary>
        /// 删除文件信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task DeleteFile(DeleteFileInput input)
        {
            // TODO: 增加删除权限校验的接口，先确认权限再删

            var entity = await _fileObjectManager.GetOrNullAsync(input.Id);
            if(entity == null)
            {
                return;
            }

            //TODO: 更换配置获取方法
            //Para para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, null);

            //await _fileUploadHelper.OnDeleted(para.UpFileInfo, entity.FileUrl, entity.Id);

            await _fileObjectManager.TryDeleteFile(entity.FileUrl, entity.Id, _fileLibraryManager);

            await _fileObjectManager.FileObjectRepository.HardDeleteAsync(entity);

            await _fileObjectManager.RemoveTemporaryFileAsync();

            await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(entity.ContentId, entity.Id, OptionType.Delete, AbpSession);
        }


        public async Task SetNewSorNumber(NewFileSortNumberInput input)
        {
            Para para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, null);

            var ob = await _fileObjectManager.GetAsync(input.Id);
            ob.Order = input.NewNumber;
            await _fileObjectManager.UpdateAsync(ob);

            await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(ob.ContentId, ob.Id, OptionType.Update, AbpSession);

            await _fileObjectManager.RemoveTemporaryFileAsync();
        }

        public async Task SetStatus(SetFileStatusInput input)
        {
            Para para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, null);

            var idList = input.IdList;
            var list = await _fileObjectManager.FileObjectRepository.GetAllListAsync(t => idList.Contains(t.Id));
            foreach (var item in list)
            {
                item.IsActive = input.IsActive;
                await _fileUploadHelper.RemoveStaticPageAndSyncFileObject(item.ContentId, item.Id, OptionType.Update, AbpSession);
            }

            await _fileObjectManager.RemoveTemporaryFileAsync();
        }

        #endregion

        #region 上传配置查询

        /// <summary>
        /// 获取配置模板信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<FileObjectConfigDto>> GetConfigInfo(UploadConfigInput input)
        {
            var listConfig = new List<FileObjectConfigDto>();
            var upfileTemplate = await _fileUploadHelper.GetUploadConfigurationAsync(input.CustomKey);

            for (int i = 0; i < upfileTemplate.Count; i++)
            {
                listConfig.Add(FileUploadExtension.TranslateToDto(upfileTemplate[i]));
            }

            return listConfig;
        }

        public async Task<FileObjectConfigDto> GetSingleConfigInfo(UploadConfigInput input)
        {
            var para = await _fileUploadHelper.GetUpFileConfig(input.CustomKey, input.Alias);

            return FileUploadExtension.TranslateToDto(para);
        }

        #endregion


        public async Task SwapObjectSortAsync(SwapSortNumberInput input)
        {
            var idList = input.IdOrderList.Select(c => c.Id).ToList();
            var orderList = input.IdOrderList.Select(c => c.Order).ToList();

            if (idList.Count == 0)
            {
                return;
            }

            var list = await _fileObjectManager.FileObjectRepository.GetAllListAsync(c => idList.Contains(c.Id));

            foreach (var item in list)
            {
                var index = idList.FindIndex(c => c == item.Id);
                if (index != -1)
                {
                    item.Order = orderList[index];
                }
            }
        }

        #region Private

        private IQueryable<FileInfoDto> GetFileObjectQuery(GetFileInfoInput input)
        {
            var query = from f in _fileObjectManager.FileObjectRepository.GetAll()
                        where f.ContentId == input.ContentId && f.Alias == input.Alias
                        orderby f.Order,f.CreationTime
                        select new FileInfoDto
                        {
                            Id = f.Id,
                            FileUrl = f.FileUrl,
                            PreViewImage = f.PreViewImage,
                            Size = f.Size,
                            FileName = f.FileName,
                            exName = f.exName,
                            Title = f.Title,
                            Alias = f.Alias,
                            Order = f.Order,
                            IsActive = f.IsActive,
                            Status = true ,
                            CreationTime = f.CreationTime
                        };
            return query;
        }

        #endregion
    }
}
